#include "NodeVisitor.h"

using namespace std;

void HierarchyCheckingVisitor::visit(CompilationUnitNode *n) {
	n->DeleteOfType("PackageDeclaration");
	n->DeleteOfType("SingleTypeImportDeclaration");
	n->DeleteOfType("TypeImportOnDemandDeclaration");
}

void HierarchyCheckingVisitor::visit(ClassDeclarationNode *n) {


	//The underlying data of the elements changed. Rebuild the set before we continue
	set<TypedNode*, compare_decls> old = n->elements_in_scope[DeclarationNode::Method];
	n->elements_in_scope[DeclarationNode::Method].clear();
	for (set<TypedNode*, compare_decls>::iterator i = old.begin(); i != old.end(); ++i)
		n->elements_in_scope[DeclarationNode::Method].insert(*i);

	//Check to make sure its immediate surroundings (supers/interfaces) exist/ have necessary properties
	CheapCheck(n);
	if (error) return;

	set<DeclarationNode*> in_hierarchy;
	CheckStructure(n, in_hierarchy);

	if (error) return;

	set<MethodDeclarationNode*, compare_decls> methods;
	InheritMethods(n, methods);
}

void HierarchyCheckingVisitor::visit(InterfaceDeclarationNode *n) {
	visit((ClassDeclarationNode*)n);
}

void HierarchyCheckingVisitor::CheapCheck(ClassDeclarationNode *n) {
	if (n->super) {
		if (n->super->Type() == "InterfaceDeclaration") {
			error = true;
			return;
		} else {
			if (n->super->modifier & ModifiedNode::Final) {
				error = true;
				return;
			}
		}
	}

	CheapCheck((InterfaceDeclarationNode*)n);
}

void HierarchyCheckingVisitor::CheapCheck(InterfaceDeclarationNode *n) {
	for (set<InterfaceDeclarationNode*, compare_decls>::iterator it = n->interfaces.begin(); it != n->interfaces.end(); ++it) {
		if ((**it).Type() != "ClassDeclaration") continue;
		error = true;
		return;
	}
}

void HierarchyCheckingVisitor::CheckStructure(ClassDeclarationNode *n, set<DeclarationNode*> &in_hierarchy) {
	if (!n) return;

	if (in_hierarchy.insert(n).second) CheckStructure(n->super, in_hierarchy);
	else {
		error = true;
		return;
	}

	CheckStructure((InterfaceDeclarationNode*)n, in_hierarchy);

}
void HierarchyCheckingVisitor::CheckStructure(InterfaceDeclarationNode *n, set<DeclarationNode*> &in_hierarchy) {
	if (!n) return;

	set<InterfaceDeclarationNode*> repeating_interfaces;
	for (set<InterfaceDeclarationNode*, compare_decls>::iterator it = n->interfaces.begin(); it != n->interfaces.end(); ++it) {
		if (in_hierarchy.insert(*it).second) {
			CheckStructure(*it, in_hierarchy);
			in_hierarchy.erase(*it);
		//This isn't acyclic
		} else error = true;

		//An interface is repeated in this class/interface
		if (!repeating_interfaces.insert(*it).second) error = true;

		if (error) return;
	}
}

void HierarchyCheckingVisitor::InheritMethods(ClassDeclarationNode *n, set<MethodDeclarationNode*, compare_decls> &inherited_methods) {
	if (!n) return;

	//If you're a root class/interface or you've already been resolved, return your methods
	if (resolved_inheritance.find(n) != resolved_inheritance.end() || (!n->super && n->interfaces.empty())) {
		resolved_inheritance.insert(n);
		inherited_methods = GetMethods(n);
		return;
	}

	//You automatically get everything from your super
	InheritMethods(n->super, inherited_methods);

	//Check the methods we got from our superclass against the ones from interfaces
	for (set<InterfaceDeclarationNode*, compare_decls>::iterator it = n->interfaces.begin(); it != n->interfaces.end(); ++it) {
		set<MethodDeclarationNode*, compare_decls> abstract_methods;
		InheritMethods((ClassDeclarationNode*)*it, abstract_methods);
		for (set<MethodDeclarationNode*, compare_decls>::iterator it = abstract_methods.begin(); it != abstract_methods.end(); ++it) {
			//Inherit any interface methods that aren't already inherited 
			set<MethodDeclarationNode*, compare_decls>::iterator super_method = inherited_methods.find(*it);
			if (super_method == inherited_methods.end()) inherited_methods.insert(*it);
			//super is implementing the interface. They need same return types
			else {
				string return1 = (**it).type_package_name + (**it).type_name;
				string return2 = (**super_method).type_package_name + (**super_method).type_name;
				error |= return1 != return2;
				if (error) return;

				

				//If the super-method is not abstract, then it can't be static
				if (((**super_method).modifier & ModifiedNode::Abstract) == 0x0) {
					error |= ((**super_method).modifier & ModifiedNode::Static) != 0x0;

					if ((**it).access & ModifiedNode::Public && (**super_method).access & ModifiedNode::Protected) error = true;
				//The super-method is abstract, and the interface is potentially more accessible. Make the super-method just as accessible
				} else if( ((**it).access & ModifiedNode::Public) != 0x0 ) {
					//Since both methods are abstract and have the same signature/return type. Take the one with the greatest accessibility
					(**super_method).access = ModifiedNode::Public;
				}
			}
		}
	}

	if (inherited_methods.empty()) {
		resolved_inheritance.insert(n);
		inherited_methods = GetMethods(n);
		return;
	}

	bool isClass = (n->Type() == "ClassDeclaration");

	bool is_abstract_class = isClass && (n->modifier & ModifiedNode::Abstract);

	//Check our current methods
	set<TypedNode*, compare_decls> &methods = n->elements_in_scope[DeclarationNode::Method];
	for (set<TypedNode*, compare_decls>::iterator it = methods.begin(); it != methods.end(); ++it) {
		if (isClass && ((MethodDeclarationNode*)*it)->modifier & ModifiedNode::Abstract) error |= !is_abstract_class;
	}

	//Check that we can properly override and inherit methods
	for (set<MethodDeclarationNode*, compare_decls>::iterator it = inherited_methods.begin(); !error && it != inherited_methods.end(); ++it) {
		set<TypedNode*, compare_decls>::iterator defined_method = methods.find(*it);
		if (defined_method == methods.end()) {
			methods.insert(*it);
			//If we inherit an abstract method, we need to be abstract (if we're a class)
			if (isClass && (**it).modifier & ModifiedNode::Abstract) 
				error |= !is_abstract_class;
		} else {
			MethodDeclarationNode *m = (MethodDeclarationNode*)*defined_method;
			string return1 = m->type_package_name + m->type_name;
			string return2 = (**it).type_package_name + (**it).type_name;

			//Must have the same return types
			if (return1 != return2) error = true;
			//Can't override a final method
			else if ((**it).modifier & ModifiedNode::Final) error = true;
			//A non-static method cannot override a static method
			else if (((**it).modifier & ModifiedNode::Static) != (m->modifier & ModifiedNode::Static)) error = true;
			//Can't reduce visibility
			else if ((**it).access & ModifiedNode::Public && m->access & ModifiedNode::Protected) error = true;
		}
	}

	resolved_inheritance.insert(n);
	inherited_methods = GetMethods(n);

}

set<MethodDeclarationNode*, compare_decls> HierarchyCheckingVisitor::GetMethods(ClassDeclarationNode *n) {
	std::set<MethodDeclarationNode*, compare_decls> methods;

	if (!n) return methods;

	if (n->elements_in_scope.find(DeclarationNode::Method) != n->elements_in_scope.end()) {

		set<TypedNode*, compare_decls> &elems = n->elements_in_scope[DeclarationNode::Method];
		for (set<TypedNode*, compare_decls>::iterator it = elems.begin(); it != elems.end(); ++it) {
			if (((ParentNode*)*it)->Type() == "ConstructorDeclaration") continue;
			methods.insert((MethodDeclarationNode*)*it);
		}
	}

	return methods;
}